#pragma once
#include <vector>
#include <memory>
#include <glm/glm.hpp>
#include <boost/any.hpp>

class IGameObject;
class IRenderPass;
class IGUI;
class CModel;
class CMainGUI;
class CCamera;

namespace Graphics
{
	namespace App
	{
		void   initApp();
		void   updateApp();
		int    getFramesPerSecond();
		double getDeltaTime();
		double getCurrentTime();
		double getFrameRateInMilliSecond();
		void   adjustFrameRate(double vAdjustFrameRate);
	}

	namespace WINDOW_KEYWORD
	{
		int  getWindowWidth();
		int  getWindowHeight();
		void setWindowSize(int vWidth, int vHeight, bool vIsViewportSizeChangedWithWindow = true);
		void setViewportSize(int vWidth, int vHeight);
		void setSampleNum(int vSampleNum);
		void setIsCursorDisable(bool vIsCursorDisable);
		void setWindowTitle(const std::string& vWindowTitle);
	}

	namespace COMPONENT_CONFIG
	{
		void setIsEnableGUI(bool vIsEnableGUI);
	}

	namespace Camera
	{
		double		getMainCameraFov();
		double		getMainCameraNear();
		double		getMainCameraFar();
		glm::dvec3 getMainCameraLookAtPos();
		glm::dvec3 getMainCameraFront();
		const glm::dvec3& getMainCameraPos();
		const glm::dvec3& getMainCameraUp();
		glm::mat4  getMainCameraProjectionMatrix();
		glm::mat4  getMainCameraViewMatrix();
		glm::mat4  getMainCameraPrevViewMatrix();
		void setMainCameraPos(glm::dvec3 vCameraPos);
		void setMainCameraFront(glm::dvec3 vCameraFront);
		void setMainCameraFarPlane(double vFarPlane);
		void setMainCameraMoveSpeed(double vMoveSpeed);
		void setMainCameraFov(double vFov);
		void setIsEnableCursor(bool vIsEnableCursor);
	}

	namespace ResourceManager
	{
		int								 getOrCreateScreenQuadVAO();
		int								 getOrCreateCubeVAO();
		const std::shared_ptr<CMainGUI>&	 getOrCreateMainGUI();
		const std::shared_ptr<IGameObject>& getGameObjectByName(const std::string &vGameObjectName);
		const std::shared_ptr<CModel>&		 getOrCreateModel(const std::string &vModelPath);
		const boost::any&					 getSharedDataByName(const std::string &vDataName);

		void registerSharedData(const std::string& vDataName, boost::any vData);
		void registerRenderPass(const std::shared_ptr<IRenderPass> &vRenderPass);
		void registerGameObject(const std::shared_ptr<IGameObject> &vGameObject);
		void registerSubGUI(const std::shared_ptr<IGUI> &vSubGUI);

		void updateSharedDataByName(const std::string& vDataName, const boost::any& vData);

		template <typename TDataType>
		TDataType getSharedDataByName(const std::string &vDataName)		//no return const &, because any_cast will allocate one temporary variable
		{
			return boost::any_cast<TDataType>(getSharedDataByName(vDataName));
		}

		template <typename TDataType>
		TDataType getSharedDataByName(const std::string &vDataName, const boost::any& vDataValueWhenLastGet, bool &vioDataChanged)
		{
			auto Data = boost::any_cast<TDataType>(getSharedDataByName(vDataName));
			auto LastData = boost::any_cast<TDataType>(vDataValueWhenLastGet);
			vioDataChanged = !(LastData == Data);
			return Data;
		}
	}

	namespace InputManager
	{
		int getKeyStatus(int vKey);
		int getMouseStatus(int vKey);
		void pumpCursorPos(double& voPosX, double& voPosY);
	}
}